--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
tests/backend/test_integrity_extensive.py v4.4.0-rc.1 (3e06f582) Text, 16.64 KB
Tff7b72import T7ee787shutil
Tff7b72import T7ee787tempfile
Tff7b72import T7ee787unittest
Tff7b72import T7ee787os
Tff7b72import T7ee787sqlite3
Tff7b72import T7ee787json
Tff7b72from T7ee787pathlib Tff7b72import Te6edf3Path
Tff7b72from T7ee787hypothesis Tff7b72import Te6edf3givenTb4b4b4, Te6edf3strategies Tff7b72as Te6edf3stTb4b4b4, Te6edf3settingsTb4b4b4, Te6edf3HealthCheck
Tff7b72from T7ee787meshchatxT7ee787.T7ee787srcT7ee787.T7ee787backendT7ee787.T7ee787integrity_manager Tff7b72import Te6edf3IntegrityManager
Tff7b72class T56d364TestIntegrityManagerExtensiveTb4b4b4(Te6edf3unittestTff7b72.Td2a8ffTestCaseTb4b4b4)Tb4b4b4:
Tff7b72def Td2a8ffsetUpTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
Tff7b72selfTff7b72.Td2a8fftest_dir Tff7b72= Te6edf3PathTb4b4b4(Te6edf3tempfileTff7b72.Td2a8ffmkdtempTb4b4b4(Tb4b4b4)Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffdb_path Tff7b72= Tff7b72selfTff7b72.Td2a8fftest_dir Tff7b72/ Ta5d6ff"Ta5d6ffdatabase.dbTa5d6ff"
Tff7b72selfTff7b72.Td2a8ffstorage_dir Tff7b72= Tff7b72selfTff7b72.Td2a8fftest_dir Tff7b72/ Ta5d6ff"Ta5d6ffstorageTa5d6ff"
Tff7b72selfTff7b72.Td2a8ffstorage_dirTff7b72.Td2a8ffmkdirTb4b4b4(Tb4b4b4)
T8b949e# Create a valid SQLite database
Te6edf3conn Tff7b72= Te6edf3sqlite3Tff7b72.Td2a8ffconnectTb4b4b4(Tff7b72selfTff7b72.Td2a8ffdb_pathTb4b4b4)
Te6edf3connTff7b72.Td2a8ffexecuteTb4b4b4(Ta5d6ff"Ta5d6ffCREATE TABLE data (id INTEGER PRIMARY KEY, val TEXT)Ta5d6ff"Tb4b4b4)
Te6edf3connTff7b72.Td2a8ffexecuteTb4b4b4(Ta5d6ff"Ta5d6ffINSERT INTO data (val) VALUES (Ta5d6ff'Ta5d6ffinitialTa5d6ff'Ta5d6ff)Ta5d6ff"Tb4b4b4)
Te6edf3connTff7b72.Td2a8ffcommitTb4b4b4(Tb4b4b4)
Te6edf3connTff7b72.Td2a8ffcloseTb4b4b4(Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffmanager Tff7b72= Te6edf3IntegrityManagerTb4b4b4(Tff7b72selfTff7b72.Td2a8fftest_dirTb4b4b4, Tff7b72selfTff7b72.Td2a8ffdb_pathTb4b4b4)
Tff7b72def Td2a8fftearDownTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
Te6edf3shutilTff7b72.Td2a8ffrmtreeTb4b4b4(Tff7b72selfTff7b72.Td2a8fftest_dirTb4b4b4)
Tff7b72def Td2a8fftest_entropy_mathematical_boundsTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
T8b949e"""Verify entropy stays within [0, 8] for any byte sequence."""
T8b949e# Test empty
Te6edf3empty_file Tff7b72= Tff7b72selfTff7b72.Td2a8fftest_dir Tff7b72/ Ta5d6ff"Ta5d6ffemptyTa5d6ff"
Te6edf3empty_fileTff7b72.Td2a8fftouchTb4b4b4(Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertEqualTb4b4b4(Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ff_calculate_entropyTb4b4b4(Te6edf3empty_fileTb4b4b4)Tb4b4b4, T79c0ff0Tb4b4b4)
T8b949e# Test single byte repeated (minimum entropy)
Te6edf3zero_entropy_file Tff7b72= Tff7b72selfTff7b72.Td2a8fftest_dir Tff7b72/ Ta5d6ff"Ta5d6ffzero_entropyTa5d6ff"
Tff7b72with Tffa657openTb4b4b4(Te6edf3zero_entropy_fileTb4b4b4, Ta5d6ff"Ta5d6ffwbTa5d6ff"Tb4b4b4) Tff7b72as Te6edf3fTb4b4b4:
Te6edf3fTff7b72.Td2a8ffwriteTb4b4b4(Ta5d6ffbTa5d6ff"Ta5d6ffAAAAAAAATa5d6ff" Tff7b72* T79c0ff100Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertEqualTb4b4b4(Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ff_calculate_entropyTb4b4b4(Te6edf3zero_entropy_fileTb4b4b4)Tb4b4b4, T79c0ff0Tb4b4b4)
T8b949e# Test all 256 bytes (maximum entropy)
Te6edf3max_entropy_file Tff7b72= Tff7b72selfTff7b72.Td2a8fftest_dir Tff7b72/ Ta5d6ff"Ta5d6ffmax_entropyTa5d6ff"
Tff7b72with Tffa657openTb4b4b4(Te6edf3max_entropy_fileTb4b4b4, Ta5d6ff"Ta5d6ffwbTa5d6ff"Tb4b4b4) Tff7b72as Te6edf3fTb4b4b4:
Te6edf3fTff7b72.Td2a8ffwriteTb4b4b4(Tffa657bytesTb4b4b4(Tffa657rangeTb4b4b4(T79c0ff256Tb4b4b4)Tb4b4b4)Tb4b4b4)
T8b949e# log2(256) = 8
Tff7b72selfTff7b72.Td2a8ffassertAlmostEqualTb4b4b4(
Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ff_calculate_entropyTb4b4b4(Te6edf3max_entropy_fileTb4b4b4)Tb4b4b4, T79c0ff8.0Tb4b4b4, Te6edf3placesTff7b72=T79c0ff5
Tb4b4b4)
Tf0883e@settingsTb4b4b4(Te6edf3suppress_health_checkTff7b72=Tb4b4b4[Te6edf3HealthCheckTff7b72.Td2a8fftoo_slowTb4b4b4]Tb4b4b4, Te6edf3deadlineTff7b72=Tff7b72NoneTb4b4b4)
Tf0883e@givenTb4b4b4(Te6edf3stTff7b72.Td2a8ffbinaryTb4b4b4(Te6edf3min_sizeTff7b72=T79c0ff1Tb4b4b4, Te6edf3max_sizeTff7b72=T79c0ff1024Tb4b4b4)Tb4b4b4)
Tff7b72def Td2a8fftest_entropy_propertyTb4b4b4(Tff7b72selfTb4b4b4, Te6edf3dataTb4b4b4)Tb4b4b4:
T8b949e"""Property: Entropy is always between 0 and 8 for non-empty data."""
Te6edf3temp_file Tff7b72= Tff7b72selfTff7b72.Td2a8fftest_dir Tff7b72/ Ta5d6ff"Ta5d6ffprop_testTa5d6ff"
Tff7b72with Tffa657openTb4b4b4(Te6edf3temp_fileTb4b4b4, Ta5d6ff"Ta5d6ffwbTa5d6ff"Tb4b4b4) Tff7b72as Te6edf3fTb4b4b4:
Te6edf3fTff7b72.Td2a8ffwriteTb4b4b4(Te6edf3dataTb4b4b4)
Te6edf3entropy Tff7b72= Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ff_calculate_entropyTb4b4b4(Te6edf3temp_fileTb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertGreaterEqualTb4b4b4(Te6edf3entropyTb4b4b4, T79c0ff0Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertLessEqualTb4b4b4(Te6edf3entropyTb4b4b4, T79c0ff8.000000000000002Tb4b4b4) T8b949e# Float precision
Tff7b72def Td2a8fftest_db_structural_tamper_detectionTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
T8b949e"""Simulate actual SQLite corruption that bypasses hash-only checks."""
Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffsave_manifestTb4b4b4(Tb4b4b4)
T8b949e# Corrupt the database file header or internal structure
T8b949e# Overwriting the first few bytes (SQLite header) is a guaranteed fail
Tff7b72with Tffa657openTb4b4b4(Tff7b72selfTff7b72.Td2a8ffdb_pathTb4b4b4, Ta5d6ff"Ta5d6ffr+bTa5d6ff"Tb4b4b4) Tff7b72as Te6edf3fTb4b4b4:
Te6edf3fTff7b72.Td2a8ffseekTb4b4b4(T79c0ff0Tb4b4b4)
Te6edf3fTff7b72.Td2a8ffwriteTb4b4b4(Ta5d6ffbTa5d6ff"Ta5d6ffNOTASQLITEFILETa5d6ff"Tb4b4b4)
Te6edf3is_okTb4b4b4, Te6edf3issues Tff7b72= Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffcheck_integrityTb4b4b4(Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertFalseTb4b4b4(Te6edf3is_okTb4b4b4, Ta5d6fffTa5d6ff"Ta5d6ffIntegrity should fail. Issues: Tffd700{Te6edf3issuesTffd700}Ta5d6ff"Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertTrueTb4b4b4(
Tffa657anyTb4b4b4(
Ta5d6ff"Ta5d6ffDatabase structural issueTa5d6ff" Tff7b72in Te6edf3i Tff7b72or Ta5d6ff"Ta5d6ffDatabase structural anomalyTa5d6ff" Tff7b72in Te6edf3i
Tff7b72for Te6edf3i Tff7b72in Te6edf3issues
Tb4b4b4)Tb4b4b4,
Ta5d6fffTa5d6ff"Ta5d6ffExpected structural issue in: Tffd700{Te6edf3issuesTffd700}Ta5d6ff"Tb4b4b4,
Tb4b4b4)
Tff7b72def Td2a8fftest_entropy_shift_detectionTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
T8b949e"""Test detection of content type change (e.g. replacing text with random bytes)."""
T8b949e# 1. Start with a highly structured file (low entropy)
T8b949e# Use a non-critical filename to trigger the entropy check branch
Te6edf3data_file Tff7b72= Tff7b72selfTff7b72.Td2a8fftest_dir Tff7b72/ Ta5d6ff"Ta5d6ffuser_data.binTa5d6ff"
Tff7b72with Tffa657openTb4b4b4(Te6edf3data_fileTb4b4b4, Ta5d6ff"Ta5d6ffwbTa5d6ff"Tb4b4b4) Tff7b72as Te6edf3fTb4b4b4:
Te6edf3fTff7b72.Td2a8ffwriteTb4b4b4(Ta5d6ffbTa5d6ff"Ta5d6ffATa5d6ff" Tff7b72* T79c0ff5000Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffsave_manifestTb4b4b4(Tb4b4b4)
T8b949e# 2. Replace with high-entropy data (random bytes)
Tff7b72with Tffa657openTb4b4b4(Te6edf3data_fileTb4b4b4, Ta5d6ff"Ta5d6ffwbTa5d6ff"Tb4b4b4) Tff7b72as Te6edf3fTb4b4b4:
Te6edf3fTff7b72.Td2a8ffwriteTb4b4b4(Te6edf3osTff7b72.Td2a8ffurandomTb4b4b4(T79c0ff5000Tb4b4b4)Tb4b4b4)
Te6edf3is_okTb4b4b4, Te6edf3issues Tff7b72= Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffcheck_integrityTb4b4b4(Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertFalseTb4b4b4(Te6edf3is_okTb4b4b4, Ta5d6fffTa5d6ff"Ta5d6ffIntegrity should fail. Issues: Tffd700{Te6edf3issuesTffd700}Ta5d6ff"Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertTrueTb4b4b4(
Tffa657anyTb4b4b4(Ta5d6ff"Ta5d6ffNon-linear content shiftTa5d6ff" Tff7b72in Te6edf3i Tff7b72or Ta5d6ff"Ta5d6ffEntropy ΔTa5d6ff" Tff7b72in Te6edf3i Tff7b72for Te6edf3i Tff7b72in Te6edf3issuesTb4b4b4)Tb4b4b4,
Ta5d6fffTa5d6ff"Ta5d6ffExpected entropy shift in: Tffd700{Te6edf3issuesTffd700}Ta5d6ff"Tb4b4b4,
Tb4b4b4)
Tff7b72def Td2a8fftest_ignore_patterns_extensiveTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
T8b949e"""Verify all volatile LXMF/RNS patterns are correctly filtered."""
Te6edf3volatile_files Tff7b72= Tb4b4b4[
Ta5d6ff"Ta5d6fflxmf_router/lxmf/outbound_stamp_costsTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6fflxmf_router/storage/some_volatile_fileTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6fflxmf_router/announces/ann_dataTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6fflxmf_router/tmp/uploadingTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffdatabase.db-walTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffdatabase.db-shmTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffsomething.tmpTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ff.DS_StoreTa5d6ff"Tb4b4b4,
Tb4b4b4]
Tff7b72for Te6edf3v Tff7b72in Te6edf3volatile_filesTb4b4b4:
Te6edf3rel_path Tff7b72= Te6edf3PathTb4b4b4(Te6edf3vTb4b4b4)
Te6edf3full_path Tff7b72= Tff7b72selfTff7b72.Td2a8fftest_dir Tff7b72/ Te6edf3rel_path
Te6edf3full_pathTff7b72.Td2a8ffparentTff7b72.Td2a8ffmkdirTb4b4b4(Te6edf3parentsTff7b72=Tff7b72TrueTb4b4b4, Te6edf3exist_okTff7b72=Tff7b72TrueTb4b4b4)
Te6edf3full_pathTff7b72.Td2a8fftouchTb4b4b4(Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertTrueTb4b4b4(
Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ff_should_ignoreTb4b4b4(Tffa657strTb4b4b4(Te6edf3rel_pathTb4b4b4)Tb4b4b4)Tb4b4b4, Ta5d6fffTa5d6ff"Ta5d6ffFailed to ignore Tffd700{Te6edf3vTffd700}Ta5d6ff"
Tb4b4b4)
Tff7b72def Td2a8fftest_critical_file_protectionTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
T8b949e"""Ensure identity and config changes are always treated as critical."""
Te6edf3id_file Tff7b72= Tff7b72selfTff7b72.Td2a8fftest_dir Tff7b72/ Ta5d6ff"Ta5d6ffidentityTa5d6ff"
Te6edf3id_fileTff7b72.Td2a8ffwrite_textTb4b4b4(Ta5d6ff"Ta5d6ffsecure_keyTa5d6ff"Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffsave_manifestTb4b4b4(Tb4b4b4)
T8b949e# Minor modification (stays low entropy)
Te6edf3id_fileTff7b72.Td2a8ffwrite_textTb4b4b4(Ta5d6ff"Ta5d6ffsecure_kezTa5d6ff"Tb4b4b4)
Te6edf3is_okTb4b4b4, Te6edf3issues Tff7b72= Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffcheck_integrityTb4b4b4(Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertFalseTb4b4b4(Te6edf3is_okTb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertTrueTb4b4b4(Tffa657anyTb4b4b4(Ta5d6ff"Ta5d6ffCritical security componentTa5d6ff" Tff7b72in Te6edf3i Tff7b72for Te6edf3i Tff7b72in Te6edf3issuesTb4b4b4)Tb4b4b4)
Tff7b72def Td2a8fftest_missing_file_detectionTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
T8b949e"""Verify missing files are detected even if not critical."""
Te6edf3misc_file Tff7b72= Tff7b72selfTff7b72.Td2a8fftest_dir Tff7b72/ Ta5d6ff"Ta5d6ffmisc.txtTa5d6ff"
Te6edf3misc_fileTff7b72.Td2a8ffwrite_textTb4b4b4(Ta5d6ff"Ta5d6ffdataTa5d6ff"Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffsave_manifestTb4b4b4(Tb4b4b4)
Te6edf3misc_fileTff7b72.Td2a8ffunlinkTb4b4b4(Tb4b4b4)
Te6edf3is_okTb4b4b4, Te6edf3issues Tff7b72= Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffcheck_integrityTb4b4b4(Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertFalseTb4b4b4(Te6edf3is_okTb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertTrueTb4b4b4(Tffa657anyTb4b4b4(Ta5d6ff"Ta5d6ffFile missing: misc.txtTa5d6ff" Tff7b72in Te6edf3i Tff7b72for Te6edf3i Tff7b72in Te6edf3issuesTb4b4b4)Tb4b4b4)
Tff7b72def Td2a8fftest_manifest_versioningTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
T8b949e"""Verify the manifest includes the new version and metadata fields."""
Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffsave_manifestTb4b4b4(Tb4b4b4)
Tff7b72with Tffa657openTb4b4b4(Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffmanifest_pathTb4b4b4) Tff7b72as Te6edf3fTb4b4b4:
Te6edf3manifest Tff7b72= Te6edf3jsonTff7b72.Td2a8ffloadTb4b4b4(Te6edf3fTb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertEqualTb4b4b4(Te6edf3manifestTb4b4b4[Ta5d6ff"Ta5d6ffversionTa5d6ff"Tb4b4b4]Tb4b4b4, T79c0ff2Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertInTb4b4b4(Ta5d6ff"Ta5d6ffmetadataTa5d6ff"Tb4b4b4, Te6edf3manifestTb4b4b4)
T8b949e# Check if database metadata exists
Te6edf3db_rel Tff7b72= Tffa657strTb4b4b4(Tff7b72selfTff7b72.Td2a8ffdb_pathTff7b72.Td2a8ffrelative_toTb4b4b4(Tff7b72selfTff7b72.Td2a8fftest_dirTb4b4b4)Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertInTb4b4b4(Te6edf3db_relTb4b4b4, Te6edf3manifestTb4b4b4[Ta5d6ff"Ta5d6ffmetadataTa5d6ff"Tb4b4b4]Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertInTb4b4b4(Ta5d6ff"Ta5d6ffentropyTa5d6ff"Tb4b4b4, Te6edf3manifestTb4b4b4[Ta5d6ff"Ta5d6ffmetadataTa5d6ff"Tb4b4b4]Tb4b4b4[Te6edf3db_relTb4b4b4]Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertInTb4b4b4(Ta5d6ff"Ta5d6ffsizeTa5d6ff"Tb4b4b4, Te6edf3manifestTb4b4b4[Ta5d6ff"Ta5d6ffmetadataTa5d6ff"Tb4b4b4]Tb4b4b4[Te6edf3db_relTb4b4b4]Tb4b4b4)
Tff7b72def Td2a8fftest_database_size_divergenceTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
T8b949e"""Verify size changes are caught when hash changes but entropy is similar."""
Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffsave_manifestTb4b4b4(Tb4b4b4)
T8b949e# Grow the database with similar content
Te6edf3conn Tff7b72= Te6edf3sqlite3Tff7b72.Td2a8ffconnectTb4b4b4(Tff7b72selfTff7b72.Td2a8ffdb_pathTb4b4b4)
Te6edf3connTff7b72.Td2a8ffexecuteTb4b4b4(Ta5d6ff"Ta5d6ffINSERT INTO data (val) VALUES (?)Ta5d6ff"Tb4b4b4, Tb4b4b4(Ta5d6ff"Ta5d6ffmore contentTa5d6ff" Tff7b72* T79c0ff100Tb4b4b4,Tb4b4b4)Tb4b4b4)
Te6edf3connTff7b72.Td2a8ffcommitTb4b4b4(Tb4b4b4)
Te6edf3connTff7b72.Td2a8ffcloseTb4b4b4(Tb4b4b4)
Te6edf3is_okTb4b4b4, Te6edf3issues Tff7b72= Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffcheck_integrityTb4b4b4(Tb4b4b4)
T8b949e# Even if entropy shift is low, hash and size changed
Tff7b72if Tff7b72not Te6edf3is_okTb4b4b4:
Tff7b72selfTff7b72.Td2a8ffassertTrueTb4b4b4(Tffa657anyTb4b4b4(Ta5d6ff"Ta5d6ffDatabaseTa5d6ff" Tff7b72in Te6edf3i Tff7b72for Te6edf3i Tff7b72in Te6edf3issuesTb4b4b4)Tb4b4b4)
T8b949e# ------------------------------------------------------------------
T8b949e# Corrupt / malformed manifest
T8b949e# ------------------------------------------------------------------
Tff7b72def Td2a8fftest_corrupt_manifest_jsonTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
T8b949e"""check_integrity must not crash on invalid JSON in the manifest."""
Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffsave_manifestTb4b4b4(Tb4b4b4)
Tff7b72with Tffa657openTb4b4b4(Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffmanifest_pathTb4b4b4, Ta5d6ff"Ta5d6ffwTa5d6ff"Tb4b4b4) Tff7b72as Te6edf3fTb4b4b4:
Te6edf3fTff7b72.Td2a8ffwriteTb4b4b4(Ta5d6ff"Ta5d6ff{{Ta5d6ff{Ta5d6ffNOT JSON!!!Ta5d6ff"Tb4b4b4)
Te6edf3is_okTb4b4b4, Te6edf3issues Tff7b72= Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffcheck_integrityTb4b4b4(Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertFalseTb4b4b4(Te6edf3is_okTb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertTrueTb4b4b4(Tffa657anyTb4b4b4(Ta5d6ff"Ta5d6ffIntegrity check failedTa5d6ff" Tff7b72in Te6edf3i Tff7b72for Te6edf3i Tff7b72in Te6edf3issuesTb4b4b4)Tb4b4b4)
Tff7b72def Td2a8fftest_empty_manifest_fileTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
T8b949e"""check_integrity must handle a 0-byte manifest gracefully."""
Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffsave_manifestTb4b4b4(Tb4b4b4)
Tff7b72with Tffa657openTb4b4b4(Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffmanifest_pathTb4b4b4, Ta5d6ff"Ta5d6ffwTa5d6ff"Tb4b4b4) Tff7b72as Te6edf3fTb4b4b4:
Te6edf3fTff7b72.Td2a8fftruncateTb4b4b4(T79c0ff0Tb4b4b4)
Te6edf3is_okTb4b4b4, Te6edf3issues Tff7b72= Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffcheck_integrityTb4b4b4(Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertFalseTb4b4b4(Te6edf3is_okTb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertTrueTb4b4b4(Tffa657anyTb4b4b4(Ta5d6ff"Ta5d6ffIntegrity check failedTa5d6ff" Tff7b72in Te6edf3i Tff7b72for Te6edf3i Tff7b72in Te6edf3issuesTb4b4b4)Tb4b4b4)
Tff7b72def Td2a8fftest_manifest_missing_keysTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
T8b949e"""Manifest with valid JSON but missing expected keys should not crash."""
Tff7b72with Tffa657openTb4b4b4(Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffmanifest_pathTb4b4b4, Ta5d6ff"Ta5d6ffwTa5d6ff"Tb4b4b4) Tff7b72as Te6edf3fTb4b4b4:
Te6edf3jsonTff7b72.Td2a8ffdumpTb4b4b4(Tb4b4b4{Ta5d6ff"Ta5d6ffversionTa5d6ff"Tb4b4b4: T79c0ff2Tb4b4b4}Tb4b4b4, Te6edf3fTb4b4b4)
Te6edf3is_okTb4b4b4, Te6edf3issues Tff7b72= Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffcheck_integrityTb4b4b4(Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertTrueTb4b4b4(Te6edf3is_ok Tff7b72or Tffa657isinstanceTb4b4b4(Te6edf3issuesTb4b4b4, Tffa657listTb4b4b4)Tb4b4b4)
T8b949e# ------------------------------------------------------------------
T8b949e# Hash consistency
T8b949e# ------------------------------------------------------------------
Tff7b72def Td2a8fftest_hash_file_consistencyTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
T8b949e"""Same file content must always produce the same hash."""
Te6edf3h1 Tff7b72= Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ff_hash_fileTb4b4b4(Tff7b72selfTff7b72.Td2a8ffdb_pathTb4b4b4)
Te6edf3h2 Tff7b72= Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ff_hash_fileTb4b4b4(Tff7b72selfTff7b72.Td2a8ffdb_pathTb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertEqualTb4b4b4(Te6edf3h1Tb4b4b4, Te6edf3h2Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertIsNotNoneTb4b4b4(Te6edf3h1Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertEqualTb4b4b4(Tffa657lenTb4b4b4(Te6edf3h1Tb4b4b4)Tb4b4b4, T79c0ff64Tb4b4b4) T8b949e# SHA-256 hex length
Tff7b72def Td2a8fftest_hash_file_missingTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
T8b949e"""_hash_file on a missing path must return None."""
Te6edf3result Tff7b72= Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ff_hash_fileTb4b4b4(Tff7b72selfTff7b72.Td2a8fftest_dir Tff7b72/ Ta5d6ff"Ta5d6ffdoes_not_exist.binTa5d6ff"Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertIsNoneTb4b4b4(Te6edf3resultTb4b4b4)
T8b949e# ------------------------------------------------------------------
T8b949e# DB integrity check
T8b949e# ------------------------------------------------------------------
Tff7b72def Td2a8fftest_check_db_integrity_validTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
T8b949e"""_check_db_integrity on a valid DB returns (True, 'ok')."""
Te6edf3okTb4b4b4, Te6edf3msg Tff7b72= Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ff_check_db_integrityTb4b4b4(Tff7b72selfTff7b72.Td2a8ffdb_pathTb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertTrueTb4b4b4(Te6edf3okTb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertEqualTb4b4b4(Te6edf3msgTb4b4b4, Ta5d6ff"Ta5d6ffokTa5d6ff"Tb4b4b4)
Tff7b72def Td2a8fftest_check_db_integrity_not_sqliteTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
T8b949e"""_check_db_integrity on a non-SQLite file returns (False, ...)."""
Te6edf3bad Tff7b72= Tff7b72selfTff7b72.Td2a8fftest_dir Tff7b72/ Ta5d6ff"Ta5d6ffbad.dbTa5d6ff"
Te6edf3badTff7b72.Td2a8ffwrite_textTb4b4b4(Ta5d6ff"Ta5d6ffthis is not sqliteTa5d6ff"Tb4b4b4)
Te6edf3okTb4b4b4, Te6edf3msg Tff7b72= Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ff_check_db_integrityTb4b4b4(Te6edf3badTb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertFalseTb4b4b4(Te6edf3okTb4b4b4)
Tff7b72def Td2a8fftest_check_db_integrity_missingTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
T8b949e"""_check_db_integrity on missing file returns (False, ...)."""
Te6edf3okTb4b4b4, Te6edf3msg Tff7b72= Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ff_check_db_integrityTb4b4b4(Tff7b72selfTff7b72.Td2a8fftest_dir Tff7b72/ Ta5d6ff"Ta5d6ffgone.dbTa5d6ff"Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertFalseTb4b4b4(Te6edf3okTb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertInTb4b4b4(Ta5d6ff"Ta5d6ffdoes not existTa5d6ff"Tb4b4b4, Te6edf3msgTb4b4b4)
Tff7b72def Td2a8fftest_check_db_integrity_empty_fileTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
T8b949e"""_check_db_integrity on a 0-byte file should not crash."""
Te6edf3empty_db Tff7b72= Tff7b72selfTff7b72.Td2a8fftest_dir Tff7b72/ Ta5d6ff"Ta5d6ffempty.dbTa5d6ff"
Te6edf3empty_dbTff7b72.Td2a8fftouchTb4b4b4(Tb4b4b4)
Te6edf3okTb4b4b4, Te6edf3msg Tff7b72= Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ff_check_db_integrityTb4b4b4(Te6edf3empty_dbTb4b4b4)
T8b949e# SQLite treats a 0-byte file as a valid empty database
Tff7b72selfTff7b72.Td2a8ffassertIsInstanceTb4b4b4(Te6edf3okTb4b4b4, Tffa657boolTb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertIsInstanceTb4b4b4(Te6edf3msgTb4b4b4, Tffa657strTb4b4b4)
T8b949e# ------------------------------------------------------------------
T8b949e# Entropy threshold boundaries
T8b949e# ------------------------------------------------------------------
Tff7b72def Td2a8fftest_entropy_threshold_db_just_belowTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
T8b949e"""DB entropy delta of 0.99 should NOT trigger anomaly warning."""
Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffsave_manifestTb4b4b4(Tb4b4b4)
Tff7b72with Tffa657openTb4b4b4(Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffmanifest_pathTb4b4b4) Tff7b72as Te6edf3fTb4b4b4:
Te6edf3manifest Tff7b72= Te6edf3jsonTff7b72.Td2a8ffloadTb4b4b4(Te6edf3fTb4b4b4)
Te6edf3db_rel Tff7b72= Tffa657strTb4b4b4(Tff7b72selfTff7b72.Td2a8ffdb_pathTff7b72.Td2a8ffrelative_toTb4b4b4(Tff7b72selfTff7b72.Td2a8fftest_dirTb4b4b4)Tb4b4b4)
Te6edf3real_entropy Tff7b72= Te6edf3manifestTb4b4b4[Ta5d6ff"Ta5d6ffmetadataTa5d6ff"Tb4b4b4]Tb4b4b4[Te6edf3db_relTb4b4b4]Tb4b4b4[Ta5d6ff"Ta5d6ffentropyTa5d6ff"Tb4b4b4]
Te6edf3manifestTb4b4b4[Ta5d6ff"Ta5d6ffmetadataTa5d6ff"Tb4b4b4]Tb4b4b4[Te6edf3db_relTb4b4b4]Tb4b4b4[Ta5d6ff"Ta5d6ffentropyTa5d6ff"Tb4b4b4] Tff7b72= Te6edf3real_entropy Tff7b72+ T79c0ff0.99
Tff7b72with Tffa657openTb4b4b4(Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffmanifest_pathTb4b4b4, Ta5d6ff"Ta5d6ffwTa5d6ff"Tb4b4b4) Tff7b72as Te6edf3fTb4b4b4:
Te6edf3jsonTff7b72.Td2a8ffdumpTb4b4b4(Te6edf3manifestTb4b4b4, Te6edf3fTb4b4b4)
Te6edf3is_okTb4b4b4, Te6edf3issues Tff7b72= Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffcheck_integrityTb4b4b4(Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertFalseTb4b4b4(
Tffa657anyTb4b4b4(Ta5d6ff"Ta5d6ffstructural anomalyTa5d6ff" Tff7b72in Te6edf3i Tff7b72for Te6edf3i Tff7b72in Te6edf3issuesTb4b4b4)Tb4b4b4,
Ta5d6fffTa5d6ff"Ta5d6ffShould not flag anomaly at delta=0.99: Tffd700{Te6edf3issuesTffd700}Ta5d6ff"Tb4b4b4,
Tb4b4b4)
Tff7b72def Td2a8fftest_entropy_threshold_db_just_aboveTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
T8b949e"""DB entropy delta of 1.01 should trigger anomaly warning."""
Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffsave_manifestTb4b4b4(Tb4b4b4)
Tff7b72with Tffa657openTb4b4b4(Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffmanifest_pathTb4b4b4) Tff7b72as Te6edf3fTb4b4b4:
Te6edf3manifest Tff7b72= Te6edf3jsonTff7b72.Td2a8ffloadTb4b4b4(Te6edf3fTb4b4b4)
Te6edf3db_rel Tff7b72= Tffa657strTb4b4b4(Tff7b72selfTff7b72.Td2a8ffdb_pathTff7b72.Td2a8ffrelative_toTb4b4b4(Tff7b72selfTff7b72.Td2a8fftest_dirTb4b4b4)Tb4b4b4)
Te6edf3real_entropy Tff7b72= Te6edf3manifestTb4b4b4[Ta5d6ff"Ta5d6ffmetadataTa5d6ff"Tb4b4b4]Tb4b4b4[Te6edf3db_relTb4b4b4]Tb4b4b4[Ta5d6ff"Ta5d6ffentropyTa5d6ff"Tb4b4b4]
Te6edf3manifestTb4b4b4[Ta5d6ff"Ta5d6ffmetadataTa5d6ff"Tb4b4b4]Tb4b4b4[Te6edf3db_relTb4b4b4]Tb4b4b4[Ta5d6ff"Ta5d6ffentropyTa5d6ff"Tb4b4b4] Tff7b72= Te6edf3real_entropy Tff7b72+ T79c0ff1.01
T8b949e# Also change the file hash so it triggers the comparison path
Te6edf3manifestTb4b4b4[Ta5d6ff"Ta5d6fffilesTa5d6ff"Tb4b4b4]Tb4b4b4[Te6edf3db_relTb4b4b4] Tff7b72= Ta5d6ff"Ta5d6ff0Ta5d6ff" Tff7b72* T79c0ff64
Tff7b72with Tffa657openTb4b4b4(Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffmanifest_pathTb4b4b4, Ta5d6ff"Ta5d6ffwTa5d6ff"Tb4b4b4) Tff7b72as Te6edf3fTb4b4b4:
Te6edf3jsonTff7b72.Td2a8ffdumpTb4b4b4(Te6edf3manifestTb4b4b4, Te6edf3fTb4b4b4)
Te6edf3is_okTb4b4b4, Te6edf3issues Tff7b72= Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffcheck_integrityTb4b4b4(Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertFalseTb4b4b4(Te6edf3is_okTb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertTrueTb4b4b4(
Tffa657anyTb4b4b4(Ta5d6ff"Ta5d6ffstructural anomalyTa5d6ff" Tff7b72in Te6edf3i Tff7b72or Ta5d6ff"Ta5d6ffEntropyTa5d6ff" Tff7b72in Te6edf3i Tff7b72for Te6edf3i Tff7b72in Te6edf3issuesTb4b4b4)Tb4b4b4,
Ta5d6fffTa5d6ff"Ta5d6ffShould flag anomaly at delta=1.01: Tffd700{Te6edf3issuesTffd700}Ta5d6ff"Tb4b4b4,
Tb4b4b4)
Tff7b72def Td2a8fftest_entropy_threshold_file_1_49_no_flagTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
T8b949e"""Non-DB file entropy delta of 1.49 should NOT trigger content shift."""
Te6edf3data_file Tff7b72= Tff7b72selfTff7b72.Td2a8fftest_dir Tff7b72/ Ta5d6ff"Ta5d6ffpayload.binTa5d6ff"
Te6edf3data_fileTff7b72.Td2a8ffwrite_bytesTb4b4b4(Ta5d6ffbTa5d6ff"Ta5d6ffXTa5d6ff" Tff7b72* T79c0ff2000Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffsave_manifestTb4b4b4(Tb4b4b4)
Tff7b72with Tffa657openTb4b4b4(Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffmanifest_pathTb4b4b4) Tff7b72as Te6edf3fTb4b4b4:
Te6edf3manifest Tff7b72= Te6edf3jsonTff7b72.Td2a8ffloadTb4b4b4(Te6edf3fTb4b4b4)
Te6edf3rel Tff7b72= Tffa657strTb4b4b4(Te6edf3data_fileTff7b72.Td2a8ffrelative_toTb4b4b4(Tff7b72selfTff7b72.Td2a8fftest_dirTb4b4b4)Tb4b4b4)
Te6edf3real_entropy Tff7b72= Te6edf3manifestTb4b4b4[Ta5d6ff"Ta5d6ffmetadataTa5d6ff"Tb4b4b4]Tb4b4b4[Te6edf3relTb4b4b4]Tb4b4b4[Ta5d6ff"Ta5d6ffentropyTa5d6ff"Tb4b4b4]
Te6edf3manifestTb4b4b4[Ta5d6ff"Ta5d6ffmetadataTa5d6ff"Tb4b4b4]Tb4b4b4[Te6edf3relTb4b4b4]Tb4b4b4[Ta5d6ff"Ta5d6ffentropyTa5d6ff"Tb4b4b4] Tff7b72= Te6edf3real_entropy Tff7b72+ T79c0ff1.49
Te6edf3manifestTb4b4b4[Ta5d6ff"Ta5d6fffilesTa5d6ff"Tb4b4b4]Tb4b4b4[Te6edf3relTb4b4b4] Tff7b72= Ta5d6ff"Ta5d6ff0Ta5d6ff" Tff7b72* T79c0ff64
Tff7b72with Tffa657openTb4b4b4(Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffmanifest_pathTb4b4b4, Ta5d6ff"Ta5d6ffwTa5d6ff"Tb4b4b4) Tff7b72as Te6edf3fTb4b4b4:
Te6edf3jsonTff7b72.Td2a8ffdumpTb4b4b4(Te6edf3manifestTb4b4b4, Te6edf3fTb4b4b4)
Te6edf3is_okTb4b4b4, Te6edf3issues Tff7b72= Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffcheck_integrityTb4b4b4(Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertFalseTb4b4b4(
Tffa657anyTb4b4b4(Ta5d6ff"Ta5d6ffNon-linear content shiftTa5d6ff" Tff7b72in Te6edf3i Tff7b72for Te6edf3i Tff7b72in Te6edf3issuesTb4b4b4)Tb4b4b4,
Ta5d6fffTa5d6ff"Ta5d6ffShould not flag content shift at delta=1.49: Tffd700{Te6edf3issuesTffd700}Ta5d6ff"Tb4b4b4,
Tb4b4b4)
Tff7b72def Td2a8fftest_entropy_threshold_file_1_51_flagsTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
T8b949e"""Non-DB file entropy delta of 1.51 should trigger content shift."""
Te6edf3data_file Tff7b72= Tff7b72selfTff7b72.Td2a8fftest_dir Tff7b72/ Ta5d6ff"Ta5d6ffpayload.binTa5d6ff"
Te6edf3data_fileTff7b72.Td2a8ffwrite_bytesTb4b4b4(Ta5d6ffbTa5d6ff"Ta5d6ffXTa5d6ff" Tff7b72* T79c0ff2000Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffsave_manifestTb4b4b4(Tb4b4b4)
Tff7b72with Tffa657openTb4b4b4(Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffmanifest_pathTb4b4b4) Tff7b72as Te6edf3fTb4b4b4:
Te6edf3manifest Tff7b72= Te6edf3jsonTff7b72.Td2a8ffloadTb4b4b4(Te6edf3fTb4b4b4)
Te6edf3rel Tff7b72= Tffa657strTb4b4b4(Te6edf3data_fileTff7b72.Td2a8ffrelative_toTb4b4b4(Tff7b72selfTff7b72.Td2a8fftest_dirTb4b4b4)Tb4b4b4)
Te6edf3real_entropy Tff7b72= Te6edf3manifestTb4b4b4[Ta5d6ff"Ta5d6ffmetadataTa5d6ff"Tb4b4b4]Tb4b4b4[Te6edf3relTb4b4b4]Tb4b4b4[Ta5d6ff"Ta5d6ffentropyTa5d6ff"Tb4b4b4]
Te6edf3manifestTb4b4b4[Ta5d6ff"Ta5d6ffmetadataTa5d6ff"Tb4b4b4]Tb4b4b4[Te6edf3relTb4b4b4]Tb4b4b4[Ta5d6ff"Ta5d6ffentropyTa5d6ff"Tb4b4b4] Tff7b72= Te6edf3real_entropy Tff7b72+ T79c0ff1.51
Te6edf3manifestTb4b4b4[Ta5d6ff"Ta5d6fffilesTa5d6ff"Tb4b4b4]Tb4b4b4[Te6edf3relTb4b4b4] Tff7b72= Ta5d6ff"Ta5d6ff0Ta5d6ff" Tff7b72* T79c0ff64
Tff7b72with Tffa657openTb4b4b4(Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffmanifest_pathTb4b4b4, Ta5d6ff"Ta5d6ffwTa5d6ff"Tb4b4b4) Tff7b72as Te6edf3fTb4b4b4:
Te6edf3jsonTff7b72.Td2a8ffdumpTb4b4b4(Te6edf3manifestTb4b4b4, Te6edf3fTb4b4b4)
Te6edf3is_okTb4b4b4, Te6edf3issues Tff7b72= Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffcheck_integrityTb4b4b4(Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertFalseTb4b4b4(Te6edf3is_okTb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertTrueTb4b4b4(
Tffa657anyTb4b4b4(Ta5d6ff"Ta5d6ffNon-linear content shiftTa5d6ff" Tff7b72in Te6edf3i Tff7b72for Te6edf3i Tff7b72in Te6edf3issuesTb4b4b4)Tb4b4b4,
Ta5d6fffTa5d6ff"Ta5d6ffShould flag content shift at delta=1.51: Tffd700{Te6edf3issuesTffd700}Ta5d6ff"Tb4b4b4,
Tb4b4b4)
T8b949e# ------------------------------------------------------------------
T8b949e# DB outside storage_dir
T8b949e# ------------------------------------------------------------------
Tff7b72def Td2a8fftest_db_outside_storage_dirTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
T8b949e"""check_integrity must not crash when DB is outside storage_dir."""
Tff7b72import T7ee787tempfile Tff7b72as T7ee787tf
Te6edf3ext_dir Tff7b72= Te6edf3PathTb4b4b4(Te6edf3tfTff7b72.Td2a8ffmkdtempTb4b4b4(Tb4b4b4)Tb4b4b4)
Tff7b72tryTb4b4b4:
Te6edf3ext_db Tff7b72= Te6edf3ext_dir Tff7b72/ Ta5d6ff"Ta5d6ffexternal.dbTa5d6ff"
Te6edf3conn Tff7b72= Te6edf3sqlite3Tff7b72.Td2a8ffconnectTb4b4b4(Te6edf3ext_dbTb4b4b4)
Te6edf3connTff7b72.Td2a8ffexecuteTb4b4b4(Ta5d6ff"Ta5d6ffCREATE TABLE t (id INTEGER PRIMARY KEY)Ta5d6ff"Tb4b4b4)
Te6edf3connTff7b72.Td2a8ffcloseTb4b4b4(Tb4b4b4)
Te6edf3mgr Tff7b72= Te6edf3IntegrityManagerTb4b4b4(Tff7b72selfTff7b72.Td2a8fftest_dirTb4b4b4, Te6edf3ext_dbTb4b4b4)
Te6edf3mgrTff7b72.Td2a8ffsave_manifestTb4b4b4(Tb4b4b4)
Te6edf3is_okTb4b4b4, Te6edf3issues Tff7b72= Te6edf3mgrTff7b72.Td2a8ffcheck_integrityTb4b4b4(Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertTrueTb4b4b4(Te6edf3is_ok Tff7b72or Tffa657isinstanceTb4b4b4(Te6edf3issuesTb4b4b4, Tffa657listTb4b4b4)Tb4b4b4)
Tff7b72finallyTb4b4b4:
Te6edf3shutilTff7b72.Td2a8ffrmtreeTb4b4b4(Te6edf3ext_dirTb4b4b4)
T8b949e# ------------------------------------------------------------------
T8b949e# Hypothesis: entropy for any binary data
T8b949e# ------------------------------------------------------------------
Tf0883e@settingsTb4b4b4(
Te6edf3suppress_health_checkTff7b72=Tb4b4b4[Te6edf3HealthCheckTff7b72.Td2a8fftoo_slowTb4b4b4]Tb4b4b4,
Te6edf3deadlineTff7b72=Tff7b72NoneTb4b4b4,
Te6edf3derandomizeTff7b72=Tff7b72TrueTb4b4b4,
Tb4b4b4)
Tf0883e@givenTb4b4b4(Te6edf3stTff7b72.Td2a8ffbinaryTb4b4b4(Te6edf3min_sizeTff7b72=T79c0ff1Tb4b4b4, Te6edf3max_sizeTff7b72=T79c0ff4096Tb4b4b4)Tb4b4b4)
Tff7b72def Td2a8fftest_entropy_monotonic_with_unique_bytesTb4b4b4(Tff7b72selfTb4b4b4, Te6edf3dataTb4b4b4)Tb4b4b4:
T8b949e"""More unique byte values should produce higher entropy."""
Te6edf3f Tff7b72= Tff7b72selfTff7b72.Td2a8fftest_dir Tff7b72/ Ta5d6ff"Ta5d6ffhyp_entTa5d6ff"
Te6edf3fTff7b72.Td2a8ffwrite_bytesTb4b4b4(Te6edf3dataTb4b4b4)
Te6edf3e Tff7b72= Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ff_calculate_entropyTb4b4b4(Te6edf3fTb4b4b4)
Te6edf3unique Tff7b72= Tffa657lenTb4b4b4(Tffa657setTb4b4b4(Te6edf3dataTb4b4b4)Tb4b4b4)
Tff7b72if Te6edf3unique Tff7b72== T79c0ff1Tb4b4b4:
Tff7b72selfTff7b72.Td2a8ffassertAlmostEqualTb4b4b4(Te6edf3eTb4b4b4, T79c0ff0.0Tb4b4b4, Te6edf3placesTff7b72=T79c0ff5Tb4b4b4)
Tff7b72elseTb4b4b4:
Tff7b72selfTff7b72.Td2a8ffassertGreaterTb4b4b4(Te6edf3eTb4b4b4, T79c0ff0.0Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertLessEqualTb4b4b4(Te6edf3eTb4b4b4, T79c0ff8.0 Tff7b72+ T79c0ff1e-9Tb4b4b4)
T8b949e# ------------------------------------------------------------------
T8b949e# Hypothesis: save_manifest then check_integrity always consistent
T8b949e# ------------------------------------------------------------------
Tf0883e@settingsTb4b4b4(
Te6edf3suppress_health_checkTff7b72=Tb4b4b4[Te6edf3HealthCheckTff7b72.Td2a8fftoo_slowTb4b4b4]Tb4b4b4,
Te6edf3deadlineTff7b72=Tff7b72NoneTb4b4b4,
Te6edf3max_examplesTff7b72=T79c0ff10Tb4b4b4,
Te6edf3derandomizeTff7b72=Tff7b72TrueTb4b4b4,
Tb4b4b4)
Tf0883e@givenTb4b4b4(Te6edf3stTff7b72.Td2a8ffbinaryTb4b4b4(Te6edf3min_sizeTff7b72=T79c0ff1Tb4b4b4, Te6edf3max_sizeTff7b72=T79c0ff512Tb4b4b4)Tb4b4b4)
Tff7b72def Td2a8fftest_save_then_check_always_passesTb4b4b4(Tff7b72selfTb4b4b4, Te6edf3extra_dataTb4b4b4)Tb4b4b4:
T8b949e"""After save_manifest(), check_integrity() must pass for unchanged state."""
Te6edf3extra_file Tff7b72= Tff7b72selfTff7b72.Td2a8fftest_dir Tff7b72/ Ta5d6ff"Ta5d6ffextra.binTa5d6ff"
Te6edf3extra_fileTff7b72.Td2a8ffwrite_bytesTb4b4b4(Te6edf3extra_dataTb4b4b4)
Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffsave_manifestTb4b4b4(Tb4b4b4)
Te6edf3is_okTb4b4b4, Te6edf3issues Tff7b72= Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffcheck_integrityTb4b4b4(Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertTrueTb4b4b4(Te6edf3is_okTb4b4b4, Ta5d6fffTa5d6ff"Ta5d6ffShould pass after save. Issues: Tffd700{Te6edf3issuesTffd700}Ta5d6ff"Tb4b4b4)
Tff7b72if Tff7b72__name__ Tff7b72== Ta5d6ff"Ta5d6ff__main__Ta5d6ff"Tb4b4b4:
Te6edf3unittestTff7b72.Td2a8ffmainTb4b4b4(Tb4b4b4)
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────